#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "global.h"
#include "connections.h"

#define PORT 2020

/* Custom function for error reporting */
int _error(const char* msg, int exit_code)
{
	perror(msg);
	exit(exit_code);	
}

/* Sets the server up */
int server_set_up()
{                     
    struct sockaddr_in serveraddr;
	int yes = 1, res;
	
	connections_setup();
		    
    /* Acquiring the listener socket */
	res = socket(AF_INET, SOCK_STREAM, 0);
	if (res == -1) 
		_error("Server-socket() error.\n",1);
	connections_set_listener(res);
#ifdef DEBUG
    printf("Server-socket() is OK...\n");
#endif
    
	/*"address already in use" error message */
	res = setsockopt(connections_get_listener(), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
	if (res == -1) 
		_error("Server-setsockopt() error.\n",1);
    printf("Server-setsockopt() is OK...\n");
    
    /* bind */
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = INADDR_ANY;
    serveraddr.sin_port = htons(PORT);
	bzero(&(serveraddr.sin_zero),8);
	//memset(&(serveraddr.sin_zero), '\0', 8);
    
	res = bind(connections_get_listener(), (struct sockaddr*) &serveraddr, sizeof(serveraddr));
	if (res == -1) 
		_error("Server-bind() error.\n",1);
    printf("Server-bind() is OK...\n");
     
    /* listen */
	res = listen(connections_get_listener(), 50); 
	if (res == -1) 
		_error("Server-listen() error.\n",1);
    printf("Server-listen() is OK...\n");
    
	return 0;
}

int main(int argc, char *argv[])
{                                           
    fd_set read_set;               
	int i, res, fd_max;
	  
	server_set_up(); 
	
   	while (1) {
		connections_get_all(&read_set);
		fd_max = connections_max_fd();
		 
		res = select(fd_max+1, &read_set, NULL, NULL, NULL);
		if (res == -1) 
			_error("Server-select() error.\n",1);
		printf("Server-select() is OK...\n");
		 
		/* Run through read_set looking for sockets with incoming data pending */
		for(i = 0; i <= fd_max; i++) {
			/* Connection didn't hasn't sent data: skip */
			if (!FD_ISSET(i, &read_set)) continue;
			
			if(i == connections_get_listener())
				/* Handle new connection */
				handle_connection_request();
			else
				/* Service an existing connection */
				do_service(i);
	    }
    }
    return 0;
}

int handle_connection_request()
{
	struct sockaddr_in clientaddr;
	int new_sock, res;
	unsigned int addrlen;
	
	addrlen = sizeof(clientaddr);
	new_sock = accept(connections_get_listener(), (struct sockaddr *) &clientaddr, &addrlen);
		
	if (new_sock == -1)
		_error("Server-accept() error.\n",1);
	else {
		printf("Server-accept() is OK...\n");
		connections_new(new_sock);
		printf("Server: New connection from %s on socket %d\n", inet_ntoa(clientaddr.sin_addr), new_sock);
	}
	
	return 0;	
}      


/* Reads the incoming message from socket fd.
 * The message is interpreted in order to delegate
 * its processing to the appropiate handling function.
 *
*/
int do_service(int fd)
{
	char buf[1024];
	int nbytes;
	
	printf("\ndo_service: entering...\n");
	
	bzero(buf,1024);
	nbytes = recv(fd, buf, sizeof(buf), 0);

#if DEBUG
	printf("do_service: recv(): \n\tnbytes=%d\n\tbuf:@%s@\n",nbytes,buf);
#endif  

	if (nbytes <= 0) {
		/* Empty message or error */                         
		printf("Server: socket %d hung up\n", fd);
		if (connections_get_status(fd) == LOGGED)
			logout_user_by_fd(fd);              		
		else
			connections_close(fd);
			
	} else {                                         
  		/* The command name is extracted and the message 
		 * is delegated to the corresponding function
		 * If a parsing error occurs, action is taken.
		*/
		char* first_newline;
	
		first_newline = strstr(buf,NEW_LINE);
		if (first_newline == NULL || first_newline-buf >= nbytes) {
			  send_ill_formed_msg(fd);
			  return 1;
		}

#if DEBUG  		
		char command[50];
		bzero(command,50);		
		memcpy(command,buf,first_newline-buf);
		printf("Parse - command recived:%s@\n", command);	
#endif  
		
		/* Dispatch segun el tipo de mensaje */
		if (strncmp(buf,"login",first_newline-buf) == 0) 
		{
			handle_login_message(fd,buf,nbytes);			
		} 
		else if (strncmp(buf,"logout",first_newline-buf) == 0) 
		{  
        	handle_logout_message(fd,buf,nbytes);
		} 
		else if (strncmp(buf,"msg-post",first_newline-buf) == 0)
		{     
        	handle_msg_post_message(fd,buf,nbytes);		
		}	
		else {
			printf("Unknown\n");
   			send_unknown_msg(fd);
			return 1;
		}	

		printf("Server-do_service(): Leaving...\n\n");  
		return 0;
	}
}
